package com.william.jdbcplus.core.dialect.ddl.column.impl;

import com.william.jdbcplus.common.entity.Entity;
import com.william.jdbcplus.common.enums.FieldEnum;
import com.william.jdbcplus.core.dialect.ddl.column.IColumn;
import com.william.jdbcplus.core.meta.TableColumnInfo;
import com.william.jdbcplus.core.meta.TableHelperUtil;
import com.william.jdbcplus.core.meta.TableInfo;
import com.william.jdbcplus.core.toolkit.StringPool;
import com.william.jdbcplus.core.util.datameta.TableMetaColumnInfo;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;

/**
 * Mysql 列实现
 *
 * @author william
 */
public class MysqlColumn implements IColumn {
    /**
     * 新增字段
     *
     * @param clazz
     * @param fieldInfo
     */
    @Override
    public <M extends Entity> String add(Class<M> clazz, TableColumnInfo fieldInfo) {
        // alter table table1 add (name varchar2(30) not null);
        TableInfo tableInfo = TableHelperUtil.getTableInfo(clazz);
        StringBuffer sqlBuffer = new StringBuffer();
        sqlBuffer
                .append("alter table " + tableInfo.getTableName() + " add column ")
                .append(fieldInfo.getColumn() + StringPool.SPACE + convertToSqlColumn(fieldInfo))
                .append(" null ")
                .append(" comment '" + fieldInfo.getRemarks().replace(';', ' ') + "';");

        if (!fieldInfo.isNullable()) {
            sqlBuffer
                    .append("alter table " + tableInfo.getTableName() + " modify column ")
                    .append(fieldInfo.getColumn() + StringPool.SPACE + convertToSqlColumn(fieldInfo))
                    .append(" not null ")
                    .append(" comment '" + fieldInfo.getRemarks().replace(';', ' ') + "';");
        }

        return sqlBuffer.toString();
    }

    /**
     * 删除字段
     *
     * @param clazz
     * @param fieldInfo
     */
    @Override
    public <M extends Entity> String drop(Class<M> clazz, TableColumnInfo fieldInfo) {
        // alter table test1 drop column name;
        TableInfo tableInfo = TableHelperUtil.getTableInfo(clazz);
        StringBuffer sqlBuffer = new StringBuffer();
        sqlBuffer
                .append("alter table " + tableInfo.getTableName() + " drop column ")
                .append(fieldInfo.getColumn() + StringPool.SEMICOLON);
        return sqlBuffer.toString();
    }

    @Override
    public <M extends Entity> String rename(Class<M> clazz, TableColumnInfo fieldFrom, TableColumnInfo fieldTo) {
        TableInfo tableInfo = TableHelperUtil.getTableInfo(clazz);
        StringBuffer sqlBuffer = new StringBuffer();
        sqlBuffer
                .append("alter table " + tableInfo.getTableName() + " rename column ")
                .append(fieldFrom.getColumn() + " to " + fieldTo.getColumn() + StringPool.SEMICOLON);
        return sqlBuffer.toString();
    }

    /**
     * 修改字段
     *
     * @param clazz
     * @param fieldInfo
     */
    @Override
    public <M extends Entity> String modify(Class<M> clazz, TableColumnInfo fieldInfo, TableMetaColumnInfo metaColumn) {
        // alter table table1 modify (name varchar2(30) not null);
        TableInfo tableInfo = TableHelperUtil.getTableInfo(clazz);
        StringBuffer sqlBuffer = new StringBuffer();
        sqlBuffer
                .append("alter table " + tableInfo.getTableName() + " modify column ")
                .append(fieldInfo.getColumn() + StringPool.SPACE + convertToSqlColumn(fieldInfo));
        if (!matchNullable(clazz, fieldInfo, metaColumn)) {
            sqlBuffer.append(fieldInfo.isNullable() ? " null " : " not null ");
        }
        sqlBuffer.append(" comment '" + fieldInfo.getRemarks().replace(';', ' ') + "';");
        return sqlBuffer.toString();
    }

    @Override
    public <M extends Entity> String rebuild(Class<M> clazz, TableColumnInfo fieldInfo, TableMetaColumnInfo metaColumn) {
        TableInfo tableInfo = TableHelperUtil.getTableInfo(clazz);
        StringBuffer sqlBuffer = new StringBuffer();
        // 1、先新增表字段,字段名是旧字段加下划线_
        TableColumnInfo newFieldInfo = new TableColumnInfo(fieldInfo, fieldInfo.getColumn() + "_");
        sqlBuffer.append(add(clazz, newFieldInfo));

        // 2、复制旧字段数据到新字段上
        sqlBuffer.append("update " + tableInfo.getTableName() + " set " + newFieldInfo.getColumn() + " = " + fieldInfo.getColumn() + ";");

        // 3、删除旧字段
        sqlBuffer.append(drop(clazz, fieldInfo));

        // 4、重命名新字段为旧字段名
        sqlBuffer.append(rename(clazz, newFieldInfo, fieldInfo));

        // 5、修改字段约束
        sqlBuffer.append(modify(clazz, fieldInfo, metaColumn));

        return sqlBuffer.toString();
    }

    @Override
    public <M extends Entity> Boolean match(Class<M> clazz, TableColumnInfo fieldInfo, TableMetaColumnInfo metaColumn) {
        // 字典名不一样，返回不匹配
        if (!fieldInfo.getColumn().equalsIgnoreCase(metaColumn.getColumnName())) {
            return false;
        }

        if (!matchTypeName(clazz, fieldInfo, metaColumn)) {
            return false;
        }

        if (!matchLength(clazz, fieldInfo, metaColumn)) {
            return false;
        }

        if (!matchNullable(clazz, fieldInfo, metaColumn)) {
            return false;
        }

        if (!matchDigits(clazz, fieldInfo, metaColumn)) {
            return false;
        }

        return true;
    }

    @Override
    public <M extends Entity> Boolean matchTypeName(Class<M> clazz, TableColumnInfo fieldInfo, TableMetaColumnInfo metaColumn) {
        Class<?> javaClass = fieldInfo.getPropertyType();
        String typeName = "varchar";
        if (FieldEnum.class.isAssignableFrom(javaClass)) {
            typeName = "int";
        } else if (Boolean.class.equals(javaClass) || javaClass.getName().equals("boolean")) {
            typeName = "int";
        } else if (Short.class.equals(javaClass) || javaClass.getName().equals("short")) {
            typeName = "int";
        } else if (Integer.class.equals(javaClass) || javaClass.getName().equals("int")) {
            typeName = "int";
        } else if (Long.class.equals(javaClass) || javaClass.getName().equals("long")) {
            typeName = "bigint";
        } else if (BigInteger.class.equals(javaClass)) {
            typeName = "bigint";
        } else if (Float.class.equals(javaClass) || javaClass.getName().equals("float")) {
            typeName = "decimal";
        } else if (Double.class.equals(javaClass) || javaClass.getName().equals("double")) {
            typeName = "decimal";
        } else if (BigDecimal.class.equals(javaClass)) {
            typeName = "decimal";
        } else if (Date.class.equals(javaClass)) {
            typeName = "datetime";
        } else if (String.class.equals(javaClass)) {
            if (fieldInfo.getSize() > 4000) {
                typeName = "text";
            } else {
                typeName = "varchar";
            }
        }

        if (typeName.equalsIgnoreCase(metaColumn.getDataTypeName())) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public <M extends Entity> Boolean matchLength(Class<M> clazz, TableColumnInfo fieldInfo, TableMetaColumnInfo metaColumn) {
        Class<?> javaClass = fieldInfo.getPropertyType();
        Integer length = fieldInfo.getSize();
        if (FieldEnum.class.isAssignableFrom(javaClass)) {
            return true;
        } else if (Boolean.class.equals(javaClass) || javaClass.getName().equals("boolean")) {
            return true;
        } else if (Short.class.equals(javaClass) || javaClass.getName().equals("short")) {
            return true;
        } else if (Integer.class.equals(javaClass) || javaClass.getName().equals("int")) {
            return true;
        } else if (Long.class.equals(javaClass) || javaClass.getName().equals("long")) {
            return true;
        } else if (BigInteger.class.equals(javaClass)) {
            return true;
        } else if (Float.class.equals(javaClass) || javaClass.getName().equals("float")) {
            if (length > 38) {
                length = 38;
            }
        } else if (Double.class.equals(javaClass) || javaClass.getName().equals("double")) {
            if (length > 38) {
                length = 38;
            }
        } else if (BigDecimal.class.equals(javaClass)) {
            if (length > 38) {
                length = 38;
            }
        } else if (Date.class.equals(javaClass)) {
            return true;
        } else if (String.class.equals(javaClass)) {
            if (length > 4000) {
                return true;
            }
        }

        if (length <= metaColumn.getColumnSize()) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public <M extends Entity> Boolean matchDigits(Class<M> clazz, TableColumnInfo fieldInfo, TableMetaColumnInfo metaColumn) {
        Class<?> javaClass = fieldInfo.getPropertyType();
        Integer digits = 0;
        if (Float.class.equals(javaClass) || javaClass.getName().equals("float") || Double.class.equals(javaClass) || javaClass.getName().equals("double") || BigDecimal.class.equals(javaClass)) {
            digits = fieldInfo.getDigits();
        } else {
            return true;
        }

        if (digits.equals(metaColumn.getDecimalDigits())) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public <M extends Entity> Boolean matchNullable(Class<M> clazz, TableColumnInfo fieldInfo, TableMetaColumnInfo metaColumn) {
        if (metaColumn.getNullAble().equals(fieldInfo.isNullable())) {
            return true;
        } else {
            return false;
        }
    }

    public String convertToSqlColumn(TableColumnInfo fieldInfo) {
        Class<?> javaClass = fieldInfo.getPropertyType();
        String decimalDigits = fieldInfo.getDigits() > 0 ? "," + fieldInfo.getDigits() : "";
        int length = fieldInfo.getSize();
        if (FieldEnum.class.isAssignableFrom(javaClass)) {
            if (length > 11) {
                length = 11;
            }
            return "int" + StringPool.LEFT_BRACKET + length + StringPool.RIGHT_BRACKET;
        } else if (Boolean.class.equals(javaClass) || javaClass.getName().equals("boolean")) {
            if (length > 11) {
                length = 11;
            }
            return "int" + StringPool.LEFT_BRACKET + length + StringPool.RIGHT_BRACKET;
        } else if (Short.class.equals(javaClass) || javaClass.getName().equals("short")) {
            if (length > 11) {
                length = 11;
            }
            return "int" + StringPool.LEFT_BRACKET + length + StringPool.RIGHT_BRACKET;
        } else if (Integer.class.equals(javaClass) || javaClass.getName().equals("int")) {
            if (length > 11) {
                length = 11;
            }
            return "int" + StringPool.LEFT_BRACKET + length + StringPool.RIGHT_BRACKET;
        } else if (Long.class.equals(javaClass) || javaClass.getName().equals("long")) {
            if (length > 20) {
                length = 20;
            }
            return "bigint" + StringPool.LEFT_BRACKET + length + StringPool.RIGHT_BRACKET;
        } else if (BigInteger.class.equals(javaClass)) {
            if (length > 20) {
                length = 20;
            }
            return "bigint" + StringPool.LEFT_BRACKET + length + StringPool.RIGHT_BRACKET;
        } else if (Float.class.equals(javaClass) || javaClass.getName().equals("float")) {
            if (length > 38) {
                length = 38;
            }
            return "decimal" + StringPool.LEFT_BRACKET + length + decimalDigits + StringPool.RIGHT_BRACKET;
        } else if (Double.class.equals(javaClass) || javaClass.getName().equals("double")) {
            if (length > 38) {
                length = 38;
            }
            return "decimal" + StringPool.LEFT_BRACKET + length + decimalDigits + StringPool.RIGHT_BRACKET;
        } else if (BigDecimal.class.equals(javaClass)) {
            if (length > 38) {
                length = 38;
            }
            return "decimal" + StringPool.LEFT_BRACKET + length + decimalDigits + StringPool.RIGHT_BRACKET;
        } else if (Date.class.equals(javaClass)) {
            return "datetime";
        } else if (String.class.equals(javaClass)) {
            if (length > 4000) {
                return "text";
            } else {
                return "varchar" + StringPool.LEFT_BRACKET + length + StringPool.RIGHT_BRACKET;
            }
        }

        return "varchar(50)";
    }
}